package com.hit.client.driver.util;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.HexUtil;
import com.hit.client.driver.netty.handler.GfMessageHandler;
import com.hit.client.driver.netty.protocol.decoder.GfDecoder;
import com.hit.client.driver.netty.protocol.encoder.GfEncoder;
import com.hit.client.driver.netty.protocol.message.GfMessage;
import com.hit.comm.netty.TcpServer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.*;

@Slf4j
public class GfToolkit {
    //默认端口
    private final static int defaultPort = 1883;
    //本地server
    public static TcpServer server;
    //获取消息结果
    public static final ConcurrentHashMap<String, CompletableFuture<Boolean>> requestMap = new ConcurrentHashMap<>();

    //机器人消息通道
    public static Map<String, ChannelHandlerContext> ctxMap = new ConcurrentHashMap<>();
    //机器人远程继电器通道
    public static Map<String, ChannelHandlerContext> relayCtxMap = new ConcurrentHashMap<>();
    //换行车消息通道
    public static Map<String, ChannelHandlerContext> ctxOfCarMap = new ConcurrentHashMap<>();
    //换行车远程继电器通道
    public static Map<String, ChannelHandlerContext> relayCtxOfCarMap = new ConcurrentHashMap<>();

    public static boolean init(Integer port) {
        int bindPort = port == null ? defaultPort : port;
        if (server == null) {
            server = new TcpServer("gf", bindPort, new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel) throws Exception {
                    channel.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, 1, 0));
                    channel.pipeline().addLast("decoder", new GfDecoder());
                    channel.pipeline().addLast("encoder", new GfEncoder());
                    channel.pipeline().addLast("handler", new GfMessageHandler());
                }
            });
            ThreadUtil.execAsync(() -> {
                try {
                    server.bind();
                } catch (InterruptedException e) {
                    log.error("[gf] server bind error", e);
                }
            });
        }
        return true;
    }

    /***************************************** 通用begin ************************************************************/

    /**
     * 将字节数组转为十六进制字符串
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) { // 将字节转为无符号的十六进制字符串
            String hex = String.format("%02X", bytes[i] & 0xFF);
            hexString.append(hex);
            if (i < bytes.length - 1) {
                hexString.append(" ");
            }
        }
        return hexString.toString();
    }

    public static boolean waitForResponse(String serialNumber, GfMessage gfMessage) {
        CompletableFuture<Boolean> future = new CompletableFuture<>();
        byte[] data = gfMessage.getData();
        int mainCmd = data[0] & 0xFF;//转换为无符号
        int subCmd = data[1] & 0xFF;//转换为无符号
        String hexKey = mainCmd + subCmd + serialNumber;
        requestMap.put(hexKey, future);
        try {
            return future.get(2, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            log.warn("[gf]cmd:{} send timeout", gfMessage.getCmdHexStr());
        }

        return false;
    }

    public static void release(String serialNumber, GfMessage gfMessage, boolean result) {
        byte[] data = gfMessage.getData();
        int mainCmd = data[0] & 0xFF;//转换为无符号
        int subCmd = data[1] & 0xFF;//转换为无符号
        String hexKey = mainCmd + subCmd + serialNumber;
        CompletableFuture<Boolean> future = requestMap.remove(hexKey);
        if (future != null) {
            future.complete(result);
            log.info("[gf]cmd:{},release", gfMessage.getCmdHexStr());
        }
    }

    private static ChannelHandlerContext getCtx(String serialNumber) {
        ChannelHandlerContext ctx = ctxMap.get(serialNumber);
        if (ctx == null) {
            log.error("[gf]serialNumber:{} not found ctx", serialNumber);
        }
        return ctx;
    }

    private static ChannelHandlerContext getRelayCtx(String serialNumber) {
        ChannelHandlerContext relayCtx = relayCtxMap.get(serialNumber);
        if (relayCtx == null) {
            log.error("[gf]serialNumber:{} not found relayCtx", serialNumber);
        }
        return relayCtx;
    }

    private static ChannelHandlerContext getCtxOfCar(String serialNumber) {
        ChannelHandlerContext ctxOfCar = ctxOfCarMap.get(serialNumber);
        if (ctxOfCar == null) {
            log.error("[gf-car]serialNumber:{} not found ctxOfCar", serialNumber);
        }
        return ctxOfCar;
    }

    private static ChannelHandlerContext getRelayCtxOfCar(String serialNumber) {
        ChannelHandlerContext relayCtxOfCar = relayCtxOfCarMap.get(serialNumber);
        if (relayCtxOfCar == null) {
            log.error("[gf-car]serialNumber:{} not found relayCtxOfCar", serialNumber);
        }
        return relayCtxOfCar;
    }

    /***************************************** 通用end ************************************************************/


    /***************************************** 光伏机器人begin ************************************************************/
    /**
     * 设置机器人电机转速
     *
     * @param serialNumber 机器人序列号
     * @param qdMotorSpeed 驱动电机转速值 r/min
     * @param gsMotorSpeed 滚刷电机转速值 r/min
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setMotorSpeed(String serialNumber, int qdMotorSpeed, int gsMotorSpeed) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x01;
        ByteBuf buffer = ctx.alloc().buffer(14);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeIntLE(qdMotorSpeed);
            buffer.writeIntLE(gsMotorSpeed);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            log.info("[gf][{}]set driver speed:{} R/min,brush speed:{} R/min", serialNumber, qdMotorSpeed, gsMotorSpeed);
            return true;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置机器工作方向及次数
     *
     * @param serialNumber 机器人序列号
     * @param mode         机器人工作方向 0x01：设置正向 0x02：设置反向
     * @param workNum      工作次数
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setWorkMode(String serialNumber, byte mode, byte workNum) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        log.info("设置参数的{}--{}", mode, workNum);
        byte mainCmd = 0x79;
        byte subCmd = 0x02;
        ByteBuf buffer = ctx.alloc().buffer(8);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(mode);
            buffer.writeByte(workNum);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set work mode:{},workNum:{},{}", serialNumber, mode == (byte) 1 ? "正向" : "反向", workNum, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置机器工作情况
     *
     * @param serialNumber 机器人序列号
     * @param mode         机器人工作方向 0x01：设置正向 0x02：设置反向
     * @param qdMotorSpeed 驱动电机转速值
     * @param gsMotorSpeed 滚刷电机转速值
     * @param workNum      工作次数
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setWork(String serialNumber, byte mode, int qdMotorSpeed, int gsMotorSpeed, byte workNum) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        log.info("设置参数的{}--{}--{}--{}--{}", serialNumber, mode, qdMotorSpeed, gsMotorSpeed, workNum);
        byte mainCmd = 0x79;
        byte subCmd = 0x03;
        ByteBuf buffer = ctx.alloc().buffer(16);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(mode);
            buffer.writeIntLE(qdMotorSpeed);
            buffer.writeIntLE(gsMotorSpeed);
            buffer.writeByte(workNum);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set work:{},driver speed:{} R/min,brush speed:{} R/min,{}", serialNumber, mode == (byte) 1 ? "正向" : "反向", qdMotorSpeed, gsMotorSpeed, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置机器人心跳间隔
     *
     * @param serialNumber 机器人序列号
     * @param interval     心跳间隔 s
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setHeartbeatInterval(String serialNumber, int interval) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x04;
        ByteBuf buffer = ctx.alloc().buffer(10);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeIntLE(interval);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set heartbeat interval:{}s,{}", serialNumber, interval, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }


    /**
     * 设置光电避障使能及时间设置
     *
     * @param serialNumber 机器人序列号
     * @param enable       机器人光电避障功能 0x01：设置开启 0x02：设置关闭
     * @param interval     设置光电避障判断时间 ms
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setPhotoelectricObstacleAvoidance(String serialNumber, byte enable, int interval) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x05;
        ByteBuf buffer = ctx.alloc().buffer(11);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(enable);
            buffer.writeIntLE(interval);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set set photoelectric obstacle avoidance enable:{},interval:{}s,{}", serialNumber, enable == (byte) 0x01, interval, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }


    /**
     * 设置IO口使能
     *
     * @param serialNumber 机器人序列号
     * @param number       0x01：相机与雨刷使能  0x02：行走电机、编码器、漫反射使能 0x03：滚刷电机使能
     *                     0x04：工控机使能 0x05：推杆PE0使能 0x06：推杆PE1使能 其中：PE1置高，PE0置低，推杆深出 PE1置低，PE0置高，推杆收回
     * @param enable       IO开关设置 0x01：设置开启 0x02：设置关闭
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setIoEnable(String serialNumber, byte number, byte enable) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x06;
        ByteBuf buffer = ctx.alloc().buffer(10);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(number);
            buffer.writeByte(enable);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set set IO:{} enable:{},{}", serialNumber, number, enable, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 光伏功能使能
     *
     * @param serialNumber 机器人序列号
     * @param function     功能选择 0x01：推杆深出使能 0x02：推杆缩回使能 0x03：雨刷清洗 0x04：编码器清零
     *                     0x05：编码器设置顺时针方向为递增方向 0x06：编码器设置逆时针方向为递增方向
     *                     0x07：设置光伏板左侧为机器人起点 0x08：设置光伏板右侧为机器人起点
     *                     0x10：设置停电保护 0x11：设置堵转保护
     * @param enable       功能开关设置 0x01：设置开启 0x02：设置关闭
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setFunctionEnable(String serialNumber, byte function, byte enable) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x07;
        ByteBuf buffer = ctx.alloc().buffer(10);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(function);
            buffer.writeByte(enable);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set function:{} enable:{},{}", serialNumber, function, enable, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }


    /**
     * 设置机器行走至预设光伏板点位
     *
     * @param serialNumber 机器人序列号
     * @param sn           设置光伏板点位
     * @param qdMotorSpeed 驱动轮电机转速（R/min）
     * @param gsMotorSpeed 滚刷电机转速（R/min）
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean goPanel(String serialNumber, short sn, int qdMotorSpeed, int gsMotorSpeed) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x08;
        ByteBuf buffer = ctx.alloc().buffer(16);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeShortLE(sn);
            buffer.writeIntLE(qdMotorSpeed);
            buffer.writeIntLE(gsMotorSpeed);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]go panel:{},qd motor speed:{}R/min,gs motor speed:{}R/min,{}", serialNumber, sn, qdMotorSpeed, gsMotorSpeed, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置机器行走至预设编码值
     *
     * @param serialNumber 机器人序列号
     * @param mileage      预设编码值
     * @param qdMotorSpeed 驱动轮电机转速（R/min）
     * @param gsMotorSpeed 滚刷电机转速（R/min）
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean goMileage(String serialNumber, int mileage, int qdMotorSpeed, int gsMotorSpeed) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return false;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x09;
        ByteBuf buffer = ctx.alloc().buffer(16);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeIntLE(mileage);
            buffer.writeIntLE(qdMotorSpeed);
            buffer.writeIntLE(gsMotorSpeed);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]go mileage:{},qd motor speed:{}R/min,gs motor speed:{}R/min,{}", serialNumber, mileage, qdMotorSpeed, gsMotorSpeed, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 获取机器工作状态
     *
     * @param serialNumber 机器人序列号
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static void getRobotStatus(String serialNumber) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x11;
        ByteBuf buffer = ctx.alloc().buffer(6);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            // 获取 ByteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 上报心跳
     *
     * @param serialNumber 机器人序列号
     */
    public static void sendHeartBeat(String serialNumber) {
        ChannelHandlerContext ctx = getCtx(serialNumber);
        if (ctx == null) {
            return;
        }
        byte mainCmd = 0x79;
        byte subCmd = 0x12;
        ByteBuf buffer = ctx.alloc().buffer(6);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctx.writeAndFlush(gfMessage);
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 控制继电器
     *
     * @param serialNumber 机器人序列号
     * @param sn           1-继电器1  2-继电器2
     * @param cmd          0-关 1-开
     * @return true-发送成功 false-发送失败
     */
    public static boolean setRelayStatus(String serialNumber, byte sn, byte cmd) {
        ChannelHandlerContext relayCtx = getRelayCtx(serialNumber);
        if (relayCtx == null) {
            return false;
        }
        ByteBuf buffer = relayCtx.alloc().buffer(4);
        try {
            buffer.writeByte(0x80);
            buffer.writeByte(0x01);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(sn);
            buffer.writeByte(cmd);
            // 获取 ByteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            relayCtx.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf][{}]set relay status {},{}", serialNumber, cmd, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏  倒转
            buffer.release();
        }
    }


    /***************************************** 光伏机器人end ************************************************************/


    /***************************************** 换行车begin ************************************************************/

    /**
     * 设置换行车电机转速
     *
     * @param serialNumber 换行车序列号
     * @param qdMotorSpeed 驱动电机转速值 r/min
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setMotorSpeedOfCar(String serialNumber, int qdMotorSpeed) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x01;
        ByteBuf buffer = ctxOfCar.alloc().buffer(10);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeIntLE(qdMotorSpeed);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            log.info("[gf-car][{}]set driver speed:{} R/min", serialNumber, qdMotorSpeed);
            return true;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置换行车运动至点位
     *
     * @param serialNumber 换行车序列号
     * @param posNum       点位
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setCarMoveToPos(String serialNumber, byte posNum) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x02;
        ByteBuf buffer = ctxOfCar.alloc().buffer(7);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(posNum);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car][{}]car move to pos:{},{}", serialNumber, posNum, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }


    /**
     * 设置换行车电动推杆控制
     *
     * @param serialNumber 换行车序列号
     * @param oprType      0x00：顶部、底部缩进
     *                     0x01：顶部、底部伸出
     *                     0x02：顶部缩进
     *                     0x03：顶部伸出
     *                     0x04：底部缩进
     *                     0x05：底部伸出
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setPushStatusOfCar(String serialNumber, byte oprType) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x03;
        ByteBuf buffer = ctxOfCar.alloc().buffer(7);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(oprType);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car][{}]set car push status:{},{}", serialNumber, oprType, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置换行车心跳间隔
     *
     * @param serialNumber 换行车序列号
     * @param interval     心跳间隔 s
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setHeartbeatIntervalOfCar(String serialNumber, int interval) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x04;
        ByteBuf buffer = ctxOfCar.alloc().buffer(10);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeIntLE(interval);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car][{}]set heartbeat interval:{}s,{}", serialNumber, interval, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置换行车电机使能
     *
     * @param serialNumber 换行车序列号
     * @param oprType      0x00：未使能  0x01：使能
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setMotorEnabledOfCar(String serialNumber, byte oprType) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x05;
        ByteBuf buffer = ctxOfCar.alloc().buffer(7);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(oprType);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car][{}]set car motor enabled:{},{}", serialNumber, oprType, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 设置换行车任务
     *
     * @param serialNumber 换行车序列号
     * @param oprType      0x00：换行车初始化  0x01：换行车自动任务 0x02：交由上位机控制
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static boolean setTaskStatusOfCar(String serialNumber, byte oprType) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return false;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x06;
        ByteBuf buffer = ctxOfCar.alloc().buffer(7);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(oprType);
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car][{}]set car task status:{},{}", serialNumber, oprType, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 获取换行车工作状态
     *
     * @param serialNumber 换行车序列号
     * @return boolean true 表示设置成功，false 表示设置失败
     */
    public static void getRobotStatusOfCar(String serialNumber) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x11;
        ByteBuf buffer = ctxOfCar.alloc().buffer(6);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            // 获取 ByteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 上报心跳
     *
     * @param serialNumber 换行车序列号
     */
    public static void sendHeartBeatOfCar(String serialNumber) {
        ChannelHandlerContext ctxOfCar = getCtxOfCar(serialNumber);
        if (ctxOfCar == null) {
            return;
        }
        byte mainCmd = 0x7C;
        byte subCmd = 0x12;
        ByteBuf buffer = ctxOfCar.alloc().buffer(6);
        try {
            buffer.writeByte(mainCmd);
            buffer.writeByte(subCmd);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            // 获取 byteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            ctxOfCar.writeAndFlush(gfMessage);
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }

    /**
     * 控制换行车继电器
     *
     * @param serialNumber 换行车序列号
     * @param sn           1-继电器1  2-继电器2
     * @param cmd          0-关 1-开
     * @return true-发送成功 false-发送失败
     */
    public static boolean setRelayStatusOfCar(String serialNumber, byte sn, byte cmd) {
        ChannelHandlerContext relayCtxOfCar = getRelayCtxOfCar(serialNumber);
        if (relayCtxOfCar == null) {
            return false;
        }
        ByteBuf buffer = relayCtxOfCar.alloc().buffer(4);
        try {
            buffer.writeByte(0x7D);
            buffer.writeByte(0x01);
            buffer.writeBytes(HexUtil.decodeHex(serialNumber));
            buffer.writeByte(sn);
            buffer.writeByte(cmd);
            // 获取 ByteBuf 中的数据到字节数组中
            byte[] bytes = new byte[buffer.readableBytes()];
            buffer.getBytes(0, bytes);
            GfMessage gfMessage = new GfMessage(bytes);
            relayCtxOfCar.writeAndFlush(gfMessage);
            boolean result = waitForResponse(serialNumber, gfMessage);
            log.info("[gf-car]set relay status {},{}", cmd, result);
            return result;
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏  倒转
            buffer.release();
        }
    }

    /***************************************** 换行车end ************************************************************/

}
